# - Coding UTF8 -
#
# Networked Decision Making
# Site: http://code.google.com/p/global-decision-making-system/
#
# License Code: GPL, General Public License v. 2.0
# License Content: Creative Commons Attribution 3.0
#
# Also visit: www.web2py.com
#             or Groups: http://groups.google.com/group/web2py 
# 	For details on the web framework used for this development
#
# Developed by Russ King (newglobalstrategy@gmail.com
# Russ also blogs occasionally at proudofyourplanent.blogspot.com
# His general thinking on why this project is very important is availalbe at
# http://www.scribd.com/doc/98216626/New-Global-Strategy
#
# This controller handles submission of questiosn and confirmation that the 
# question has been submitted
# main question table - this is deliberately and consciously not normalised due to aim to deploy
# on google app engine and difficult of doing joins so up to 10 answers are hard coded into
# the question structure - not terribly elegant but there we are. Table has also been overloaded
# to an extent by including actions as a type of question - actions are special in that users
# would only ever be asked to agree or disagree with actions - there are no other possible answers

import datetime
from plugin_hradio_widget import hradio_widget


not_empty = IS_NOT_EMPTY()
continents = ['Unspecified','Africa (AF)','Asia (AS)','Europe (EU)',
                            'North America (NA)','Oceania (OC)','South America (SA)']

def sysinit():
    #session.categories = ['Unspecified']
    session.categories=[]
    categories= db(db.category.id>0).select(db.category.cat_desc, cache=(cache.ram,1200), cacheable=True).as_list()
    for x in categories:
        session.categories.append(x['cat_desc'])

def userinit():
    '''
    This initialises user variables into the session.

    It's not in ndsfunctions module as don't know how to import auth created
    in model file which is not a module.
    '''
    session.userid = auth.user
    session.continent = auth.user.continent
    session.country = auth.user.country
    session.subdivision = auth.user.subdivision
    session.level = auth.user.level

    return 

#lastly setup session variables for the user if logged in and not setup
#probably these should be elsewhere but lets leave here for now
if session.userinit is None and auth.user:
    #establish session variables for user
    userinit()
    session.userinit = True

sysinit()
#This table has been de-relationalised to continent, country, category, subdivision and activescope
#because record readcount was going off scale on google app engine for no good reason


if not request.env.web2py_runtime_gae:
    db.define_table('question',
Field('qtype','string',writable=False,
      requires=IS_IN_SET(['quest','action']),default='quest'),
Field('questiontext','text',label='Question',requires=not_empty),
Field('level','integer',default=1,writable=False),
Field('status','string',default='In Progress',writable=False, 
      requires=IS_IN_SET(['In Progress','Resolved','Agreed','Disagreed','Rejected'])),
Field('auth_userid','reference auth_user',writable=False, label='Submitter'),
Field('ext_userid','string',default='na',readable=False,writable=False, 
       label='External id eg facebook'), #not yet being used
Field('category', 'string',default ='Unspecified', label='Category',comment='Optional'),
Field('activescope', 'string', default = '1 Global', label='Active Scope'),
Field('continent', 'string',default ='Unspecified', label='Continent'),
Field('country', 'string', default='Unspecified', label='Country'),
Field('subdivision', 'string', default ='Unspecified', label='Sub-division eg State'),
Field('scopetext', 'string', writable = False, default ='1 Global',label='Scope',
        comment='Global, France, Montana etc ie a mixed field'),
Field('numanswers','integer',label='Number of Answers',default=2,
        requires=IS_IN_SET([2,3,4,5,6,7,8,9,10]) ),
Field('answers','list:string'),
Field('answercounts','list:integer',default=[0,0,0,0,0,0,0,0,0,0,0]),#always 11 items paases in element 0
Field('totanswers','integer',default=0, writable=False),
Field('correctans','integer',default=0,requires=IS_IN_SET([0,1,2,3,4,5,6,7,8,9,10]),
writable=False,label='Correct Ans'),
Field('correctanstext','string',label='Ans Text'),
Field('urgency','decimal(6,2)', default=5,writable=False, label='Urgency'),
Field('importance','decimal(6,2)', default=5,writable=False, label='Importance'),
Field('totratings','integer', default=0,writable=False),
Field('priority','decimal(6,2)', compute=lambda r: r['urgency']*r['importance'],writable=False, label='Priority'),
Field('numchallenges','integer',default=0,readable=False),
Field('numchallenged','integer',default=0,readable=False),
Field('currentrouting','string',default='std',requires=IS_IN_SET(['std','vote']),writable=False),
Field('unpanswers','integer',default=0,writable=False,readable=False),
Field('createdate','datetime', writable=False,label='Date Submitted', default=request.utcnow),
Field('resolvedate','datetime', writable=False,label='Date Resolved'),
Field('answerreasons','text',writable=False, label='Reason1'),
Field('answerreason2','text',writable=False, label='Reason2'),
Field('answerreason3','text',writable=False, label='Reason3'),
Field('numagree','integer',default=0,writable=False),
Field('numdisagree','integer',default=0,writable=False),
Field('responsible','string',label='Person or Group Responsible'),
Field('respemail','string', label='Responsible e-mail address'),
Field('respurl','string', label='Responsible website'),
Field('contactmethod','string', requires=IS_IN_SET(['email','website']),default='email'),
Field('duedate','datetime', label='Due Date', default=(request.utcnow + datetime.timedelta(days=365)) ),
Field('achieved','integer',default=0),
Field('priorquests','list:integer',default=[0]),
Field('subsquests','list:integer',default=0, label='Follow-on Questions'),
Field('challenge', 'boolean', default=False),
Field('numcomments','integer',default=0,writable=False, label='# of comments'),) #numcomments not yet used

else:
    db.define_table('question',
Field('qtype','string',writable=False,
      requires=IS_IN_SET(['quest','action']),default='quest'),
Field('questiontext','text',label='Question',requires=not_empty),
Field('level','integer',default=1,writable=False),
Field('status','string',default='In Progress',writable=False, 
      requires=IS_IN_SET(['In Progress','Resolved','Agreed','Disagreed','Rejected'])),
Field('auth_userid','reference auth_user',writable=False, label='Submitter'),
Field('ext_userid','string',default='na',readable=False,writable=False, 
       label='External id eg facebook'), #not yet being used
Field('category', 'string',default ='Unspecified', label='Category',comment='Optional'),
Field('activescope', 'string', default = '1 Global', label='Active Scope'),
Field('continent', 'string',default ='Unspecified', label='Continent'),
Field('country', 'string', default='Unspecified', label='Country'),
Field('subdivision', 'string', default ='Unspecified', label='Sub-division eg State'),
Field('scopetext', 'string', writable = False, default ='1 Global',label='Scope',
        comment='Global, France, Montana etc ie a mixed field', custom_qualifier={'indexed':False}),
Field('numanswers','integer',label='Number of Answers',default=2,
        requires=IS_IN_SET([2,3,4,5,6,7,8,9,10]), custom_qualifier={'indexed':False} ),
Field('answers','list:string'),
Field('answercounts','list:integer',default=[0,0,0,0,0,0,0,0,0,0,0]),#always 11 items paases in element 0
Field('totanswers','integer',default=0, writable=False, custom_qualifier={'indexed':False}),
Field('correctans','integer',default=0,requires=IS_IN_SET([0,1,2,3,4,5,6,7,8,9,10]),
writable=False,label='Correct Ans', custom_qualifier={'indexed':False}),
Field('correctanstext','string',label='Ans Text', custom_qualifier={'indexed':False}),
Field('urgency','decimal(6,2)', default=5,writable=False, label='Urgency', custom_qualifier={'indexed':False}),
Field('importance','decimal(6,2)', default=5,writable=False, label='Importance', custom_qualifier={'indexed':False}),
Field('totratings','integer', default=0,writable=False, custom_qualifier={'indexed':False}),
Field('priority','decimal(6,2)', compute=lambda r: r['urgency']*r['importance'],writable=False, label='Priority'),
Field('numchallenges','integer',default=0,readable=False, custom_qualifier={'indexed':False}),
Field('numchallenged','integer',default=0,readable=False, custom_qualifier={'indexed':False}),
Field('currentrouting','string',default='std',requires=IS_IN_SET(['std','vote']),writable=False, custom_qualifier={'indexed':False}),
Field('unpanswers','integer',default=0,writable=False,readable=False, custom_qualifier={'indexed':False}),
Field('createdate','datetime', writable=False,label='Date Submitted', default=request.utcnow),
Field('resolvedate','datetime', writable=False,label='Date Resolved'),
Field('answerreasons','text',writable=False, label='Reason1'),
Field('answerreason2','text',writable=False, label='Reason2'),
Field('answerreason3','text',writable=False, label='Reason3'),
Field('numagree','integer',default=0,writable=False, custom_qualifier={'indexed':False}),
Field('numdisagree','integer',default=0,writable=False, custom_qualifier={'indexed':False}),
Field('responsible','string',label='Person or Group Responsible'),
Field('respemail','string', label='Responsible e-mail address', custom_qualifier={'indexed':False}),
Field('respurl','string', label='Responsible website', custom_qualifier={'indexed':False}),
Field('contactmethod','string', requires=IS_IN_SET(['email','website']),default='email', custom_qualifier={'indexed':False}),
Field('duedate','datetime', label='Due Date', default=(request.utcnow + datetime.timedelta(days=365)) ),
Field('achieved','integer',default=0, custom_qualifier={'indexed':False}),
Field('priorquests','list:integer',default=[0]),
Field('subsquests','list:integer',default=0, label='Follow-on Questions'),
Field('challenge', 'boolean', default=False, custom_qualifier={'indexed':False}),
Field('numcomments','integer',default=0,writable=False, label='# of comments', custom_qualifier={'indexed':False}),) #numcomments not yet used


db.question.category.requires = IS_IN_SET(session.categories)
db.question.continent.requires = IS_IN_SET(continents)
db.question.activescope.requires = IS_IN_DB(db, 'scope.description', cache=(cache.ram,3600))

db.question.duedate.requires = IS_DATETIME_IN_RANGE(format=T('%Y-%m-%d %H:%M:%S'),
    minimum=datetime.datetime(2013,1,1,10,30),
    maximum=datetime.datetime(2030,12,31,11,45),
    error_message='must be YYYY-MM-DD HH:MM::SS!')

db.question.respemail.requires = IS_EMPTY_OR(IS_EMAIL())

#This table holds records for normal question answers and also for answering
#challenges and actions - in fact no obvious reason to differentiate
#the question will hold a flag to determine if under challenge but only so 
#that the challengers get credit when resolved again if the answer is 
#different

if not request.env.web2py_runtime_gae:
    db.define_table('userquestion',
    Field('questionid',db.question,writable=False),
    Field('auth_userid','reference auth_user',writable=False,readable=False),
    Field('status','string', default='In Progress',writable=False,readable=False),
    Field('level','integer',readable=False, writable=False),
    Field('answer','integer',default=0,requires=IS_IN_SET([0,1,2,3,4,5,6,7,8,9,10]),label='My Answer'),
    Field('reject','boolean'),
    Field('urgency','integer',default=5,requires=IS_IN_SET([1,2,3,4,5,6,7,8,9,10])),
    Field('importance','integer',default=5,requires=IS_IN_SET([1,2,3,4,5,6,7,8,9,10])),
    Field('score','integer',writable='False'),
    Field('answerreason', 'text', label='Reasoning' ),
    Field('ansdate','datetime',default=request.now,writable=False,readable=False ),
    Field('category', 'string', default='Unspecified', comment='Optional'),
    Field('activescope', 'string', default = '1 Global'),
    Field('continent', 'string',default = 'Unspecified', label='Continent'),
    Field('country', 'string', default = 'Unspecified', label='Country'),
    Field('subdivision', 'string',default = 'Unspecified',label='Sub-division'),
    Field('changecat', 'boolean', default=False, label= 'Change Category'),
    Field('changescope','boolean', default=False, label= 'Change Scope'),
    Field('reviewstatus','string', default='Not Reviewed', label= 'Review Status'),
    Field('resolvedate','datetime', writable=False,label='Date Resolved'))

else:
    db.define_table('userquestion',
    Field('questionid',db.question,writable=False),
    Field('auth_userid','reference auth_user',writable=False,readable=False),
    Field('status','string', default='In Progress',writable=False,readable=False),
    Field('level','integer',readable=False, writable=False),
    Field('answer','integer',default=0,requires=IS_IN_SET([0,1,2,3,4,5,6,7,8,9,10]),label='My Answer',custom_qualifier={'indexed':False}),
    Field('reject','boolean', custom_qualifier={'indexed':False}),
    Field('urgency','integer',default=5,requires=IS_IN_SET([1,2,3,4,5,6,7,8,9,10]),custom_qualifier={'indexed':False}),
    Field('importance','integer',default=5,requires=IS_IN_SET([1,2,3,4,5,6,7,8,9,10]), 
           custom_qualifier={'indexed':False}),
    Field('score','integer',writable='False', custom_qualifier={'indexed':False}),
    Field('answerreason', 'text', label='Reasoning' ),
    Field('ansdate','datetime',default=request.now,writable=False,readable=False ),
    Field('category', 'string', default='Unspecified', comment='Optional'),
    Field('activescope', 'string', default = '1 Global'),
    Field('continent', 'string',default = 'Unspecified', label='Continent'),
    Field('country', 'string', default = 'Unspecified', label='Country'),
    Field('subdivision', 'string',default = 'Unspecified',label='Sub-division'),
    Field('changecat', 'boolean', default=False, label= 'Change Category', custom_qualifier={'indexed':False}),
    Field('changescope','boolean', default=False, label= 'Change Scope', custom_qualifier={'indexed':False}),
    Field('reviewstatus','string', default='Not Reviewed', label= 'Review Status'),
    Field('resolvedate','datetime', writable=False,label='Date Resolved'))

#suggest using this to stop unnecessary indices on gae but doesn't work elsewhere so need to fix somehow
#,custom_qualifier={'indexed':False} think - retry this later in the week


#Note the setup below of de-relationalising is intended to optimise performance on GAE
#as it appears normal reference fields cannot be cached and this was creating a lot
#of database reads 
#These all removed from GAE as they were expensive in database reads and will be re-implemented
#via javascript as mostly will not need to be used

#db.userquestion.category.requires = IS_IN_DB(db, 'category.cat_desc', cache=(cache.ram,3600))

db.userquestion.category.requires = IS_IN_SET(session.categories)
db.userquestion.continent.requires = IS_IN_SET(['Unspecified','Africa (AF)','Asia (AS)','Europe (EU)',
                            'North America (NA)','Oceania (OC)','South America (SA)'])
db.userquestion.activescope.requires = IS_IN_SET(['1 Global','2 Continental','3 National', '4 Local'])


#db.userquestion.answer.widget = SQLFORM.widgets.radio.widget
#this holds details of who has challenged the answer to a question
#points are awarded for successful challenges and deducted for
#unsuccessful ones
db.define_table('questchallenge',
Field('questionid','reference question',writable=False,readable=False),
Field('auth_userid','reference auth_user',writable=False,readable=False),
Field('status','string', default='In Progress', writable=False,readable=False),
Field('challengereason', 'text' ),
Field('challengedate','datetime',default=request.now,writable=False,readable=False ))


#this holds details of who has agreed and disagreed on the answer to a question
#no points are awarded for this at present but it may be configured to prevent
#challenges if the agreement to disagreement ratio is above some point this will also 
#now support logging agreement to actions and so urgency and importance have been 
#added to this table - however they are also picked up in userquestion - thinking is
#questions will not show this but actions will ie will pick-up in one place only
#Some users may want to record agreement without ranking immediately - but will
#accept their default values for now as no way of knowing if intended or not

db.define_table('questagreement',
Field('questionid','reference question',writable=False),
Field('auth_userid','reference auth_user',writable=False),
Field('agree','integer',writable=False,readable=False),
Field('agreedate','datetime',default=request.now,writable=False,readable=False),
Field('urgency','integer',default=0,requires=IS_IN_SET([1,2,3,4,5,6,7,8,9,10])),
Field('importance','integer',default=0,requires=IS_IN_SET([1,2,3,4,5,6,7,8,9,10])),
Field('level','integer',default=1,readable=False,writable=False))

db.define_table('questurgency',
Field('questionid','reference question',writable=False),
Field('auth_userid','reference auth_user',writable=False),
Field('urgency','integer',default=5,requires=IS_IN_SET([1,2,3,4,5,6,7,8,9,10])),
Field('importance','integer',default=5,requires=IS_IN_SET([1,2,3,4,5,6,7,8,9,10])),
Field('level','integer',default=1,readable=False,writable=False))

#this holds comments for resolved questions
#it may be extended to allow comments against unresolved but not yet
#it will allow comments against actions that are proposed
#which is now a new status on actions where preceding question is not resolved
#and on follow-up questions


db.define_table('questcomment',
Field('questionid','reference question',writable=False,readable=False,
      default=session.questid),
Field('auth_userid','reference auth_user',writable=False,readable=False,
      default=auth.user_id),
Field('comment','text',requires=IS_NOT_EMPTY()),
Field('status','string',default='OK',writable=False,readable=False,requires=IS_IN_SET(['OK','NOK'])),
Field('numreject','integer',default=0,writable=False,readable=False),
Field('usersreject','list:integer',writable=False,readable=False),
Field('commentdate','datetime',default=request.utcnow,writable=False,readable=False))


#this scores parameters for scoring on the system - if you delete it then it will 
#get populated with the default values in due course there are now two additional
#fields for agreeing an action at a particular level as that will have a different 
#scoring level and users would need to choose if they want to review actions.

db.define_table('scoring',
Field('level','integer'),
Field('right','integer' ),
Field('wrong','integer' ),
Field('rightchallenge','integer' ),
Field('wrongchallenge','integer' ),
Field('rightaction','integer' ),
Field('wrongaction','integer' ),
Field('nextlevel','integer' ),
Field('submitter', 'integer' ),
format='%(level)')

#this stores a single record with a description of the scope of the system in 
#due course it may extend to cover a number of subjects but not in version 1

db.define_table('subject',
Field('shortdesc','string'),
Field('longdesc','text'))

#This table is never populated but holds settings and options for configuring
#many of the displays of actions and questions

db.define_table('viewscope',
Field('sortorder', 'string',default = '1 Priority', label = 'Sort Order'),
Field('qsortorder', 'string',default = '1 Priority', label = 'Sort Order'),
Field('asortorder', 'string',default = '1 Answer Date', label = 'Sort Order'),
Field('showscope','boolean',label='Show scope Filter',comment='Uncheck to show all'),
Field('scope', 'string',default = '1 Global'),
Field('continent', 'string',default = 'Unspecified', label='Continent'),
Field('country', 'string',default = 'Unspecified', label='Country'),
Field('subdivision', 'string',default ='Unspecified', label='Sub-division'),
Field('showcat','boolean',label='Show Category Filter',comment='Uncheck to show all'),
Field('category', 'string',default ='Unspecified', label='Category',comment='Optional'))





#lets see how the input works first
db.viewscope.category.requires = IS_IN_DB(db, 'category.cat_desc', cache=(cache.ram,3600))
db.viewscope.scope.requires = IS_IN_DB(db, 'scope.description', cache=(cache.ram,3600))
db.viewscope.continent.requires = IS_IN_SET(continents)
db.viewscope.sortorder.requires = IS_IN_SET(['1 Priority', '2 Due Date', '3 Resolved Date', '4 Submit Date',
                                             '5 Responsible']) 
db.viewscope.qsortorder.requires = IS_IN_SET(['1 Priority',  '2 Resolved Date', '3 Submit Date']) 
db.viewscope.asortorder.requires = IS_IN_SET(['1 Answer Date',  '2 Resolved Date', '3 Category']) 
db.viewscope.scope.widget = hradio_widget
db.viewscope.sortorder.widget = hradio_widget
db.viewscope.qsortorder.widget = hradio_widget
db.viewscope.asortorder.widget = hradio_widget

#This contains two standard messages one for general objective and a second
#for specific action which someone is responsible for
db.define_table('message', Field('msgtype','string'),
Field('description', 'text'),
Field('message_text','text'))

#This holds details of the top 10 companies in the world and their leaders
db.define_table('company',
Field('company_name','string',label='Corporation'),
Field('leader','string', label='Leader'),
Field('email','string', label='Leader E-mail'),
Field('website','string',label='Leader website'),
Field('contact_method','string',requires=IS_IN_SET(['email','website']),
      default='email',label='Contact Method'),
format='%(company_name)s')

#This holds details of the top 10 individuals by wealth in the world
db.define_table('individual',
Field('leader','string', label='Leader'),
Field('email','string', label='Leader E-mail'),
Field('website','string',label='Leader website'),
Field('contact_method','string',requires=IS_IN_SET(['email','website']),
       default='email',label='Contact Method'),
format='%(individual_name)s')





